﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SolverFoundation.Common;
using Microsoft.SolverFoundation.Services;

namespace CuttingStock
{
    public class CuttingStrategy
    {
        public const Int32 NumPattern = 4;
        public const Int32 NumComponent = 5;

        public static Int32[,] _patterns = new Int32[NumPattern, NumComponent] {
            { 2, 3, 1, 0, 2 },
            { 0, 4, 5, 1, 2 },
            { 3, 1, 0, 5, 1 },
            { 3, 0, 1, 2, 4 }
        };

        public static Int32[] _patternWaste = new Int32[NumPattern] { 11, 1, 22, 12 };

        /// <summary>
        /// need to call CuttingStrategy.Generate() first
        /// </summary>
        public static Int32[] patternNum = null;

        public void ShowArea(Int32[] areaofComponents)
        {
            foreach (var pa in Enumerable.Range(0,NumPattern).Select(i=>Enumerable.Range(0,NumComponent).Select(j=>_patterns[i,j]*areaofComponents[j]).Aggregate((area, a)=>area+a)))
                Console.WriteLine(pa);
        }

        public Int32[] Generate(Int32[] requiredComponents, Int32[] areaofComponents)
        {
            SolverContext context = SolverContext.GetContext();
            Model model = context.CreateModel();

            var patternNumDecisions = Enumerable.Range(0, NumPattern).Select(i => new Decision(Domain.IntegerNonnegative, String.Format("Pattern{0}", i))).ToArray();
            model.AddDecisions(patternNumDecisions);

            var excess = new Term[NumComponent];

            for (Int32 j = 0; j < NumComponent; ++j)
            {
                var acc = patternNumDecisions[0] * 0;
                for (Int32 i = 0; i < NumPattern; ++i)
                {
                    acc += _patterns[i, j] * patternNumDecisions[i];
                }
                excess[j] = acc - requiredComponents[j];
            }

            // j \in [0, NumComponent)
            var lim = Enumerable.Range(0, NumComponent).Select(j => 0 <= excess[j]).ToArray();
            model.AddConstraints("quantity_more_than_required", lim);

            var cuttingWaste = Enumerable.Range(0, NumPattern).Select(i => _patternWaste[i] * patternNumDecisions[i]).Aggregate((total, waste) => total + waste);
            var totalWaste = Enumerable.Range(0, NumComponent).Select(j => excess[j] * areaofComponents[j]).Aggregate(cuttingWaste, (total, waste) => total + waste);
            model.AddGoal("waste", GoalKind.Minimize, totalWaste);

            Solution solution = context.Solve(new SimplexDirective());

            patternNum = patternNumDecisions.Select(p => Convert.ToInt32(p.ToDouble())).ToArray();

            return patternNum;

            //var actualNum = Enumerable.Range(0, NumComponent).Select(j => Enumerable.Range(0, NumPattern).Select(i => _patterns[i, j] * patternNums[i].ToDouble()).Aggregate((sum, n) => sum + n));
            //foreach (var n in actualNum)
            //    Console.WriteLine(n);

            //Report report = solution.GetReport();

            //Console.Write("{0}", report);
            //Console.ReadLine();
        }
    }
}
